/**
 * @file snmp_server.c
 * @author Wei.Studio
 * @brief SNMP Server
 * @version 0.1
 * @date 2024-03-11
 * 
 * @copyright Copyright (c) 2024 Wei.Studio
 * 
 */



#include "snmp.h"
#include "qelib.h"
#include "udp_socket.h"



#define SNMP_LOG_DOMAN      "snmp-server"
#define snmp_debug(...)      qelog_debug(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_info(...)       qelog_info(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_notice(...)     qelog_notice(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_warning(...)    qelog_warning(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_error(...)      qelog_error(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_fatal(...)      qelog_fatal(SNMP_LOG_DOMAN, __VA_ARGS__)
#define snmp_hexdump(...)    qehex_debug(SNMP_LOG_DOMAN, __VA_ARGS__)


typedef struct
{
    udp_socket sock;
    snmp_message *request_message;
    snmp_message *response_message;
    snmp_parser parser;
} snmp_server;

static char test_string1[128] = "String1 This library is free software;"
        "you can redistribute it and/or modify it under"
        "the terms of the GNU Lesser General";

static char test_string2[128] = "String2 This library is free software;"
        "you can redistribute it and/or modify it under"
        "the terms of the GNU Lesser General";

static int integer1 = 12345;
static int integer2 = 54321;

qe_ret snmp_get_string1(void *v, int *len)
{
    snmp_info("get string: %s", test_string1);
    qe_strcpy(v, test_string1);
    *len = qe_strlen(test_string1);
    return qe_ok;
}

qe_ret snmp_set_string1(void *v, int len)
{
    qe_strcpy(test_string1, v);
    snmp_info("set string: %s", test_string1);
    return qe_ok;
}

qe_ret snmp_get_string2(void *v, int *len)
{
    snmp_info("get string: %s", test_string2);
    qe_strcpy(v, test_string2);
    *len = qe_strlen(test_string2);
    return qe_ok;
}

qe_ret snmp_set_string2(void *v, int len)
{
    qe_strcpy(test_string2, v);
    snmp_info("set string: %s", test_string2);
    return qe_ok;
}

qe_ret snmp_get_integer1(void *v, int *len)
{
    int val;
    snmp_info("get integer1: %d", integer1);
    val = qe_htonl(integer1);
    qe_memcpy(v, &val, 4);
    *len = 4;
    return qe_ok;
}

qe_ret snmp_set_integer1(void *v, int len)
{
    int val = 0;
    qe_memcpy(&val, v, len);
    integer1 = val;
    snmp_info("set integer1 %d", integer1);
    return qe_ok;
}

qe_ret snmp_get_integer2(void *v, int *len)
{
    int val;
    snmp_info("get integer2: %d", integer2);
    val = qe_htonl(integer2);
    qe_memcpy(v, &val, 4);
    *len = 4;
    return qe_ok;
}

qe_ret snmp_set_integer2(void *v, int len)
{
    int val = integer2;
    qe_memcpy(&val, v, len);
    integer2 = qe_htonl(val);
    snmp_info("set integer2 %d", integer2);
    return qe_ok;
}

static snmp_data_entry entrys[] = {
    {8, {0x2b,6,1,2,1,1,1,1}, SNMP_DTYPE_OCTET_STRING, 0, {""},
        snmp_get_string1, snmp_set_string1},
    {8, {0x2b,6,1,2,1,1,1,2}, SNMP_DTYPE_OCTET_STRING, 0, {""},
        snmp_get_string2, snmp_set_string2},
    {8, {0x2b,6,1,2,1,1,1,3}, SNMP_DTYPE_INTEGER, 0, {""},
        snmp_get_integer1, snmp_set_integer1},
    {8, {0x2b,6,1,2,1,1,1,4}, SNMP_DTYPE_INTEGER, 0, {""},
        snmp_get_integer2, snmp_set_integer2},
};



static snmp_server *snmp_server_new(const char *address, qe_u16 port, 
    qe_uint bufsz)
{
    qe_ret ret;
    snmp_server *s;

    s = qe_malloc(sizeof(snmp_server));
    if (!s) {
        snmp_error("alloc mem for server error");
        return QE_NULL;
    }
    qe_memset(s, 0x0, sizeof(snmp_server));

    ret = udp_socket_init(&s->sock, address, port);
    if (ret != qe_ok) {
        snmp_error("socket create error:%d", ret);
        qe_free(s);
        return QE_NULL;
    }
    snmp_debug("socket create ok");

    s->response_message = snmp_message_new(bufsz);
    if (!s->response_message) {
        snmp_error("create response message error");
        goto __error;
    }
    snmp_debug("response message create");

    s->request_message = snmp_message_new(bufsz);
    if (!s->request_message) {
        snmp_error("create request message error");
        goto __error;
    }
    snmp_debug("request message create");

    qe_list_init(&s->parser.values);
    s->parser.num_entrys = qe_array_size(entrys);
    s->parser.entrys     = entrys;

    return s;

__error:
    if (s) {
        udp_socket_exit(&s->sock);
        if (s->request_message)
            qe_free(s->request_message);
        if (s->response_message)
            qe_free(s->response_message);
        qe_free(s);
    }

    return QE_NULL;
}

static void snmp_response_send(snmp_server *server)
{
    qe_ret ret;
    ret = udp_socket_send(&server->sock, 
                        server->response_message->buf, 
                        server->response_message->index);

    if (ret != qe_ok) {
        snmp_error("send response error:%d", ret);
    }
}

int main(int argc, char *argv[])
{
    int n;
    int addrlen;
    qe_ret ret;

    qelog_init(QELOG_DEBUG, QELOG_DATE|QELOG_CL|QELOG_LV|QELOG_DM);

    snmp_server *server = snmp_server_new("127.0.0.1", 161, 1024);
    if (!server) {
        snmp_error("create server error");
        return -1;
    }

    snmp_info("snmp agent start");

    udp_socket_bind(&server->sock);

    while (1) {
        
        ret = udp_socket_recv(&server->sock, 
                     server->request_message->buf, 
                     server->request_message->size, 
                     &n);
        if (n <= 0 || ret != qe_ok) {
            qe_msleep(200);
            continue;
        }
        
        snmp_debug("recv:");
        snmp_hexdump(server->request_message->buf, n);
        
        ret = snmp_message_parse(&server->parser, server->request_message);
        if (ret != qe_ok) {
            snmp_error("parse message error:%d", ret);
        }

        snmp_package_response(&server->parser, server->response_message);
        snmp_response_send(server);
    }

    return 0;
}
